package listeners;

import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.ParseTreeProperty;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import org.antlr.v4.runtime.tree.TerminalNode;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Stack;

public class TestEvaluator {
    // sample calculator
    public static class Evaluator extends ExprBaseListener {
        Stack<Integer> stack = new Stack<>();

        @Override
        public void exitE(ExprParser.EContext ctx) {
            if (ctx.getChildCount() == 3) {
                // operation have 3 children
                int right = stack.pop();
                int left = stack.pop();
                if (ctx.op.getType() == ExprParser.MULT) {
                    stack.push(left * right);
                } else {
                    stack.push(left + right);
                }
            }
        }

        @Override
        public void visitTerminal(TerminalNode node) {
            Token symbol = node.getSymbol();
            if (symbol.getType() == ExprParser.INT) {
                // System.out.println("add int in evaluator " + symbol.getText());
                stack.push(Integer.parseInt(symbol.getText()));
            }
        }

    }

    // sample calculator using tree properties not stack
    public static class EvaluatorWithProps extends ExprBaseListener {
        ParseTreeProperty<Integer> values = new ParseTreeProperty<>();

        public void exitS(ExprParser.SContext ctx) {
            values.put(ctx, values.get(ctx.getChild(0)));
        }

        public void exitE(ExprParser.EContext ctx) {
            if (ctx.getChildCount() == 3) {
                System.out.println(ctx.children);
                System.out.println(ctx.getText());
                int left = values.get(ctx.e(0));
                int right = values.get(ctx.e(1));
                if (ctx.op.getType() == ExprParser.MULT) {
                    values.put(ctx, left * right);
                } else {
                    values.put(ctx, left + right);
                }
            } else {
                // INT
                values.put(ctx, values.get(ctx.getChild(0)));
                System.out.println(ctx.children);
                System.out.println(ctx.getText());
            }
        }

        public void visitTerminal(TerminalNode node) {
            Token symbol = node.getSymbol();
            if (symbol.getType() == ExprParser.INT) {
                values.put(node, Integer.parseInt(symbol.getText()));
            }
        }

    }

    public static void main(String[] args) throws IOException {
        String inputFile = null;
        if (args.length>0) {
            inputFile = args[0];
        }
        InputStream is = System.in;
        if (inputFile != null) {
            is = new FileInputStream(inputFile);
        }
        CharStream input = CharStreams.fromStream(is);
        ExprLexer lexer = new ExprLexer(input);
        CommonTokenStream tokenStream = new CommonTokenStream(lexer);
        ExprParser parser = new ExprParser(tokenStream);
        parser.setBuildParseTree(true);
        ParseTree tree = parser.s();
        System.out.println(tree.toStringTree(parser));

        ParseTreeWalker walker = new ParseTreeWalker();
        Evaluator eval = new Evaluator();
        walker.walk(eval, tree);
        System.out.println("result = " + eval.stack.pop());

        EvaluatorWithProps evaluatorWithProps = new EvaluatorWithProps();
        walker.walk(evaluatorWithProps, tree);
        System.out.println("result with tree props " + evaluatorWithProps.values.get(tree));


    }
}
